package com.shell.governor.modules.scanner;

import com.shell.bee.base.annotation.KeywordMap;
import com.shell.bee.base.utils.StringUtil;
import java.io.File;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;

public class MessageKeywordScanner implements InitializingBean {
	private final Log logger = LogFactory.getLog(getClass());
	private String scanPackagePath = "";
	public static Map<String, Class> scanKeywords = new HashMap();

	private void sanPackages(String scanpackagePaths) {
		String[] paths = scanpackagePaths.split(",");
		for (String path : paths) {
			scanPackage(path);
		}
	}

	private void scanPackage(String scanpackagePath) {
		String path = scanpackagePath.replace(".", "/");
		URL url = Thread.currentThread().getContextClassLoader().getResource(path);
		try {
			List<File> fileList = new ArrayList<File>();
			if ((url != null) && (url.toString().startsWith("file"))) {
				String filePath = URLDecoder.decode(url.getFile(), "utf-8");
				File dir = new File(filePath);
				fetchFileList(dir, fileList);
				readFile(fileList);
			} else if (isJarFile(url.toString())) {
				JarURLConnection connection = (JarURLConnection) url.openConnection();
				if (connection != null) {
					JarFile jarFile = connection.getJarFile();
					if (jarFile != null) {
						Enumeration<JarEntry> enu = jarFile.entries();
						while (enu.hasMoreElements()) {
							JarEntry entry = (JarEntry) enu.nextElement();
							if ((entry.getName().contains(".class"))
									&& (entry.getName().replaceAll("/", ".").startsWith(scanpackagePath))) {
								String className = entry.getName().substring(0, entry.getName().lastIndexOf("."))
										.replace("/", ".");
								loadClassForName(className);
							}
						}
						jarFile.close();
					}
					readFile(fileList);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void readFile(List<File> fileList) throws ClassNotFoundException {
		for (File f : fileList) {
			String fileName = f.getAbsolutePath();
			if (fileName.endsWith(".class")) {
				String nosuffixFileName = fileName.substring(8 + fileName.lastIndexOf("classes"),
						fileName.indexOf(".class"));
				String filePackage = "";
				if ("\\".equals(File.separator)) {
					filePackage = nosuffixFileName.replaceAll("\\\\", ".");
				} else {
					filePackage = nosuffixFileName.replaceAll(File.separator, ".");
				}
				loadClassForName(filePackage);
			}
		}
	}

	private void loadClassForName(String filePackage) throws ClassNotFoundException {
		Class<?> clazz = Class.forName(filePackage);
		String key = clazz.getSimpleName();
		Object annotation = clazz.getAnnotation(KeywordMap.class);
		if (annotation != null) {
			KeywordMap keywordMap = (KeywordMap) annotation;
			if (!StringUtil.emptyAndNull(keywordMap.value())) {
				key = keywordMap.value();
			}
			this.logger.info(key + " => " + clazz);
			if (scanKeywords.containsKey(key)) {
				throw new MessageKeywordConflictException("����������������������" + clazz + "��key:" + key + "������");
			}
			scanKeywords.put(key, clazz);
		}
	}

	private void fetchFileList(File dir, List<File> fileList) {
		if (dir.isDirectory()) {
			for (File f : dir.listFiles()) {
				fetchFileList(f, fileList);
			}
		} else {
			fileList.add(dir);
		}
	}

	public String getScanPackagePath() {
		return this.scanPackagePath;
	}

	public void setScanPackagePath(String scanPackagePath) {
		this.scanPackagePath = scanPackagePath;
	}

	private boolean isJarFile(String name) {
		return (name.endsWith(".jar")) || (name.contains(".jar"));
	}

	public void afterPropertiesSet() throws Exception {
		if (!StringUtil.emptyAndNull(this.scanPackagePath)) {
			this.logger.info("��������������[start]............");
			this.logger.info("������������" + this.scanPackagePath);
			sanPackages(this.scanPackagePath);
			this.logger.info("��������������[end]............");
		}
	}
}
